home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlutil.zip / CHMOD.C < prev    next >
C/C++ Source or Header  |  1990-06-17  |  8KB  |  315 lines

  1. /* chmod -- change permission modes of files
  2.    Copyright (C) 1989, 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: chmod [-Rcdfv] mode file...
  19.           mode is [ugoa...][[+-=][rwxXstugo...]...][,...] or octal number.
  20.  
  21.    Options:
  22.    -R    Recursively change modes of directory contents.
  23.    -c    Verbosely describe only files whose modes actually change.
  24.    -d    Dereference symbolic links (recursively change the modes of
  25.     directories pointed to by symbolic links).
  26.    -f    Do not print error messages about files.
  27.    -v    Verbosely describe changed modes.
  28.  
  29.    David MacKenzie <djm@ai.mit.edu> */
  30.  
  31. #include <stdio.h>
  32. #include <sys/types.h>
  33. #include "getopt.h"
  34. #include "modechange.h"
  35. #include "system.h"
  36.  
  37. #ifdef STDC_HEADERS
  38. #include <errno.h>
  39. #include <stdlib.h>
  40. #else
  41. char *malloc ();
  42. char *realloc ();
  43.  
  44. extern int errno;
  45. #endif
  46.  
  47. int lstat ();
  48. int stat ();
  49.  
  50. char *savedir ();
  51. char *xmalloc ();
  52. char *xrealloc ();
  53. int change_file_mode ();
  54. int change_dir_mode ();
  55. void describe_change ();
  56. void error ();
  57. void mode_string ();
  58. void usage ();
  59.  
  60. typedef enum
  61. {
  62.   false = 0, true = 1
  63. } boolean;
  64.  
  65. /* The name the program was run with. */
  66. char *program_name;
  67.  
  68. /* If true, change the modes of directories recursively. */
  69. boolean recurse;
  70.  
  71. /* If true, force silence (no error messages). */
  72. boolean force_silent;
  73.  
  74. /* If true, describe the modes we set. */
  75. boolean verbose;
  76.  
  77. /* If true, describe only modes that change. */
  78. boolean changes_only;
  79.  
  80. /* A pointer to either lstat or stat. */
  81. int (*xstat) ();
  82.  
  83. /* Parse the ASCII mode given on the command line into a linked list
  84.    of `struce mode_change' and apply that to each file argument. */
  85.  
  86. void
  87. main (argc, argv)
  88.      int argc;
  89.      char **argv;
  90. {
  91.   extern int optind;
  92.   struct mode_change *changes;
  93.   int errors = 0;
  94.   int modeind = 0;        /* Index of the mode argument in `argv'. */
  95.   int c;
  96.  
  97.   program_name = argv[0];
  98.   recurse = force_silent = verbose = changes_only = false;
  99.   xstat = lstat;
  100.  
  101.   while ((c = getopt (argc, argv, "RcdfvrwxXstugoa,+-=")) != EOF)
  102.     {
  103.       switch (c)
  104.     {
  105.     case 'r':
  106.     case 'w':
  107.     case 'x':
  108.     case 'X':
  109.     case 's':
  110.     case 't':
  111.     case 'u':
  112.     case 'g':
  113.     case 'o':
  114.     case 'a':
  115.     case ',':
  116.     case '+':
  117.     case '-':
  118.     case '=':
  119.       if (modeind != 0 && modeind != optind - 1)
  120.         error (1, 0, "invalid mode");
  121.       modeind = optind - 1;
  122.       break;
  123.     case 'R':
  124.       recurse = true;
  125.       break;
  126.     case 'c':
  127.       verbose = true;
  128.       changes_only = true;
  129.       break;
  130.     case 'd':
  131.       xstat = stat;
  132.       break;
  133.     case 'f':
  134.       force_silent = true;
  135.       break;
  136.     case 'v':
  137.       verbose = true;
  138.       break;
  139.     default:
  140.       usage ();
  141.     }
  142.     }
  143.  
  144.   if (modeind == 0)
  145.     modeind = optind++;
  146.   if (optind >= argc)
  147.     usage ();
  148.  
  149.   changes = mode_compile (argv[modeind], MODE_MASK_EQUALS | MODE_MASK_PLUS);
  150.   if (changes == MODE_INVALID)
  151.     error (1, 0, "invalid mode");
  152.   else if (changes == MODE_MEMORY_EXHAUSTED)
  153.     error (1, 0, "virtual memory exhausted");
  154.  
  155.   for (; optind < argc; ++optind)
  156.     errors |= change_file_mode (argv[optind], changes);
  157.  
  158.   exit (errors);
  159. }
  160.  
  161. /* Change the mode of `file' according to the list of operations `changes'.
  162.    Return 0 if successful, 1 if errors occurred. */
  163.  
  164. int
  165. change_file_mode (file, changes)
  166.      char *file;
  167.      struct mode_change *changes;
  168. {
  169.   struct stat file_stats;
  170.   unsigned short newmode;
  171.   int errors = 0;
  172.  
  173.   if ((*xstat) (file, &file_stats))
  174.     {
  175.       if (force_silent == false)
  176.     error (0, errno, "%s", file);
  177.       return 1;
  178.     }
  179. #ifdef S_IFLNK
  180.   if ((file_stats.st_mode & S_IFMT) == S_IFLNK)
  181.     return 0;
  182. #endif
  183.  
  184.   newmode = mode_adjust (file_stats.st_mode, changes);
  185.  
  186.   if (newmode != (file_stats.st_mode & 07777))
  187.     {
  188.       if (verbose)
  189.     describe_change (file, newmode, 1);
  190.       if (chmod (file, (int) newmode))
  191.     {
  192.       if (force_silent == false)
  193.         error (0, errno, "%s", file);
  194.       errors = 1;
  195.     }
  196.     }
  197.   else if (verbose && changes_only == false)
  198.     describe_change (file, newmode, 0);
  199.  
  200.   if (recurse && (file_stats.st_mode & S_IFMT) == S_IFDIR)
  201.     errors |= change_dir_mode (file, changes, &file_stats);
  202.   return errors;
  203. }
  204.  
  205. /* Recursively change the modes of the files in directory `dir'
  206.    according to the list of operations `changes'.
  207.    `statp' points to the results of lstat or stat on `dir'.
  208.    Return 0 if successful, 1 if errors occurred. */
  209.  
  210. int
  211. change_dir_mode (dir, changes, statp)
  212.      char *dir;
  213.      struct mode_change *changes;
  214.      struct stat *statp;
  215. {
  216.   char *name_space, *namep;
  217.   char *path;            /* Full path of each entry to process. */
  218.   unsigned dirlength;        /* Length of `dir' and '\0'. */
  219.   unsigned filelength;        /* Length of each pathname to process. */
  220.   unsigned pathlength;        /* Bytes allocated for `path'. */
  221.   int errors = 0;
  222.  
  223.   errno = 0;
  224.   name_space = savedir (dir, statp->st_size);
  225.   if (name_space == NULL)
  226.     {
  227.       if (errno)
  228.     {
  229.       if (force_silent == false)
  230.         error (0, errno, "%s", dir);
  231.       return 1;
  232.     }
  233.       else
  234.     error (1, 0, "virtual memory exhausted");
  235.     }
  236.  
  237.   dirlength = strlen (dir) + 1;    /* + 1 is for the trailing '/'. */
  238.   pathlength = dirlength + 1;
  239.   /* Give `path' a dummy value; it will be reallocated before first use. */
  240.   path = xmalloc (pathlength);
  241.   strcpy (path, dir);
  242.   path[dirlength - 1] = '/';
  243.  
  244.   for (namep = name_space; *namep; namep += filelength - dirlength)
  245.     {
  246.       filelength = dirlength + strlen (namep) + 1;
  247.       if (filelength > pathlength)
  248.     {
  249.       pathlength = filelength * 2;
  250.       path = xrealloc (path, pathlength);
  251.     }
  252.       strcpy (path + dirlength, namep);
  253.       errors |= change_file_mode (path, changes);
  254.     }
  255.   free (path);
  256.   free (name_space);
  257.   return errors;
  258. }
  259.  
  260. /* Tell the user the mode `mode' that file `file' has been set to;
  261.    if `changed' is zero, `file' had that mode already. */
  262.  
  263. void
  264. describe_change (file, mode, changed)
  265.      char *file;
  266.      unsigned short mode;
  267.      int changed;
  268. {
  269.   char perms[11];        /* "-rwxrwxrwx" ls-style modes. */
  270.  
  271.   mode_string (mode, perms);
  272.   perms[10] = '\0';        /* `mode_string' does not null terminate. */
  273.   if (changed)
  274.     printf ("mode of %s changed to %04o (%s)\n",
  275.         file, mode & 07777, &perms[1]);
  276.   else
  277.     printf ("mode of %s retained as %04o (%s)\n",
  278.         file, mode & 07777, &perms[1]);
  279. }
  280.  
  281. /* Allocate `n' bytes of memory dynamically, with error checking.  */
  282.  
  283. char *
  284. xmalloc (n)
  285.      unsigned n;
  286. {
  287.   char *p;
  288.  
  289.   p = malloc (n);
  290.   if (p == 0)
  291.     error (1, 0, "virtual memory exhausted");
  292.   return p;
  293. }
  294.  
  295. char *
  296. xrealloc (p, n)
  297.      char *p;
  298.      unsigned n;
  299. {
  300.   p = realloc (p, n);
  301.   if (p == 0)
  302.     error (1, 0, "virtual memory exhausted");
  303.   return p;
  304. }
  305.  
  306. void
  307. usage ()
  308. {
  309.   fprintf (stderr, "\
  310. Usage: %s [-Rcdfv] mode file...\n\
  311.        mode is [ugoa...][[+-=][rwxXstugo...]...][,...] or octal number\n",
  312.        program_name);
  313.   exit (1);
  314. }
  315.